Skip to content

[MAIN-IMP] Dashboard UI Enhancements and Performance Improvements#87

Open
moda20 wants to merge 16 commits intomasterfrom
Improvement/dashboard-ui-element-improvements
Open

[MAIN-IMP] Dashboard UI Enhancements and Performance Improvements#87
moda20 wants to merge 16 commits intomasterfrom
Improvement/dashboard-ui-element-improvements

Conversation

@moda20
Copy link
Copy Markdown
Owner

@moda20 moda20 commented Apr 2, 2026

Improvements:

  • Updated the dashboard metric card to display job metrics separately

  • Implemented virtualization for jobs table to improve performance with large datasets

  • Implemented virtualization for job event drawer to handle long lists efficiently

  • Updated advanced filtering dialog to use regex by default for most inputs

  • Adding a server status component that uses the version endpoint

  • Latest run drawer updates :

    • Adding last runs groupings by weeks/months/years and fixing duplicated requests at drawer opening
    • Updating duration calculation shown for each run (better UI)
    • Updating scrollable list and useLogs hook to better serve the last run drawer

Notes

  • [UI]: Added proper z-index to sidebar to resolve layering issues
  • [UI] : Added notification service list scrolling
  • [Note] (performance) : The virtualizer on the job event drawer needs to be manually refreshed with a setTimeout as it doesn't refresh correctly when the full list of events is updated via the event hook

Motivation and Context

How Has This Been Tested?

Screenshots (if applicable)

Add screenshots to help explain your changes.

Related Issues

Additional Context

Summary by CodeRabbit

  • New Features

    • Virtualized list/table rendering for smoother scrolling with large datasets.
    • Run grouping control (day/week/month) in the latest-runs drawer.
  • Improvements

    • Refreshed metrics bar with clearer sections and integrated error indicators.
    • Consistent 2px borders and tighter picker/popover styling.
    • Loading overlays and faster sheet transitions; improved infinite-scroll UX and list reset control.
    • Layout tweaks for sidebar and panels for better responsiveness.
  • Bug Fixes

    • Date/timestamp normalization and human-readable duration formatting.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 2, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Converted multiple list and table renderings to virtualized flows, added date utilities and metric theme tokens, adjusted UI layout/styling (borders, z-index, spacing), introduced new ref methods and controlled inputs, refined dialog handling, and updated socket log loading state.

Changes

Cohort / File(s) Summary
List Virtualization
src/components/custom/DrawerJobEvents.tsx, src/features/jobsTable/jobsTable.tsx
Replaced non-virtual lists with react-virtualizer usage: DrawerJobEvents merges latestEvents+events into allEvents and measures rows; jobsTable adds TableBodyVirtualized, uses tableContainerRef as scroll element, and renders absolutely-positioned translated rows.
DataTable API
src/features/jobsTable/jobsTable.tsx
Added optional size?: string to DataTableProps and threaded size into DataTable wrapper className.
Latest Runs Drawer & Grouping
src/components/custom/DrawerLatestRuns.tsx, src/utils/dateUtils.ts
Added run grouping by day/week/month using new getTimeIndex and formatDiff; adjusted fetching/pagination shape ({data:[], total:0}), reset/open behavior, and grouping controls.
Dashboard Metrics & Theme Tokens
src/components/custom/dashboard/statsBar.tsx, src/styles/global.css, tailwind.config.ts
Added six metric CSS vars and Tailwind color keys; refactored metric cards into MetricSection with unified error handling and responsive dividers; updated icons/titles/layout.
Flexible Input & Filtering
src/components/custom/general/MultiTypedInput.tsx, src/components/custom/jobsTable/advancedJobFilteringDialog.tsx
Made FlexibleInput type controlled/optional, conditionally show type picker, memoized handlers, effect to sync controlled type; advancedJobFilteringDialog reads nested values.*.value, uses type="regex" for some fields.
Scrollable / Infinite Loader
src/components/custom/general/ScrollableList.tsx
Added imperative resetList method to ref; changed sentinel/loader DOM to render loader only when loading true (impacting IntersectionObserver target lifecycle); updated effect deps.
Dialog Confirm Handling
src/components/custom/DrawerJobFiles.tsx, src/components/sheet-action-dialog.tsx
Added ConfirmationDialogActionType handling and handleConfirmAction wrapper to no-op on CANCEL; conditionally set SheetTitle/SheetDescription asChild when props are non-string React elements.
Socket Logs Loading State
src/lib/socketUtils.ts
Added logsLoading state returned from useSocketLogs, set/reset around getLogs async calls using .finally.
Log Files & Small Cleanups
src/components/custom/general/LogFileList.tsx, src/features/system/database.tsx
createAt now ISO-stringifies logFile.date when present; removed unused icon/card imports.
UI Styling Tweaks
src/components/ui/date-picker-presets.tsx, src/components/ui/sidebar.tsx, src/components/custom/notifications/NotificationServicesPanel.tsx, src/components/custom/ManagedSelect.tsx
Added explicit border-2 border-border to popover/select/calendar content; sidebar root gains z-50; notification panel left column gets fixed viewport height and flex/grow for scrollable list; ManagedSelect dropdown border updated.
Jobs Table Columns & Page Layout
src/features/jobsTable/interfaces.tsx, src/features/jobsTable/jobsPage.tsx
Added size/minSize constraints for columns, centered action cell, changed sentinel height to h-[1px], adjusted sticky header padding.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant DrawerJobEvents
  participant Virtualizer
  participant DOM
  User->>DrawerJobEvents: Open drawer / scroll
  DrawerJobEvents->>Virtualizer: provide count, getItemKey, estimateSize
  Virtualizer->>DOM: request measure for rendered element
  DOM-->>Virtualizer: measured size
  Virtualizer-->>DrawerJobEvents: virtual items subset
  DrawerJobEvents->>DOM: render absolute-positioned rows (translateY)
Loading
sequenceDiagram
  participant User
  participant DataTable
  participant TableBodyVirtualized
  participant Virtualizer
  participant ScrollContainer
  User->>DataTable: Scrolls table
  DataTable->>TableBodyVirtualized: supply rows & container ref
  TableBodyVirtualized->>Virtualizer: set scroll element, count, estimateSize
  Virtualizer->>ScrollContainer: observe scroll, compute visible range
  Virtualizer-->>TableBodyVirtualized: virtual items subset
  TableBodyVirtualized->>ScrollContainer: render positioned rows
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰 I hopped through lists both long and wide,
Merged events to dance and rows to glide,
Metrics donned new hues and tokens bright,
Inputs and dialogs now behave just right,
Virtual rows race on — a rabbit's delight. 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly addresses the main changes: dashboard metric card improvements, jobs table virtualization, job event drawer virtualization, and UI fixes. It accurately reflects the primary objectives.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch Improvement/dashboard-ui-element-improvements

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@moda20 moda20 changed the title [MAIN-Feat] Dashboard UI Enhancements and Performance Improvements [DEV-IMP] Dashboard UI Enhancements and Performance Improvements Apr 2, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 16

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/components/custom/general/LogFileList.tsx (1)

28-46: ⚠️ Potential issue | 🟠 Major

Complete the useMemo dependency list to avoid stale props.

props.originName and props.downloadLogFile are used inside the memoized callback but omitted from the dependency array. When these optional props change, the memoized FileList will not be recalculated, causing stale values in the rendered items.

💡 Proposed fix
-  }, [props.logFiles, props.readLogFile])
+  }, [
+    props.logFiles,
+    props.readLogFile,
+    props.downloadLogFile,
+    props.originName,
+  ])
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/custom/general/LogFileList.tsx` around lines 28 - 46, The
memoized FileList created by useMemo currently lists [props.logFiles,
props.readLogFile] but omits props.originName and props.downloadLogFile, causing
stale values when those props change; update the useMemo dependency array for
the FileList variable (the useMemo call that builds FileItemProps) to include
props.originName and props.downloadLogFile along with props.logFiles and
props.readLogFile so the memo recalculates when any of those inputs change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/components/custom/dashboard/statsBar.tsx`:
- Around line 148-173: The two MetricSection bindings are inconsistent: change
the Scheduled Jobs MetricSection to read the scheduled count from jobMetrics
(use jobMetrics.scheduled_jobs_count) instead of jobMetrics.running_jobs_count,
and change the Running Jobs MetricSection to use the snake_case running count
(jobMetrics.running_jobs_count) instead of jobMetrics.runningJobsCount so both
metrics match the payload's snake_case keys used elsewhere (e.g., job_count).
- Around line 47-50: The runtime-built Tailwind class `${bgColorLight}/5` in the
className passed to cn prevents Tailwind from generating the background opacity
utility; change the component API to accept a fully-qualified tint class (e.g.,
"bg-metric1-light/5") instead of bgColorLight and use that prop directly in the
className; update references to the prop name (e.g., tintClass or bgTint) in the
component where cn is used (and anywhere StatsBar is instantiated) and remove
any runtime string interpolation that appends "/5", preserving the hasError
branch that uses "bg-destructive/5".

In `@src/components/custom/DrawerJobEvents.tsx`:
- Around line 74-83: The virtualizer is using index-based identity which causes
remounts when latestEvents are prepended; update the useVirtualizer call that
creates rowVirtualizer to include a getItemKey function that returns a stable
unique identifier from each event object in allEvents (e.g., event.id or
event.eventId, falling back to a composite of timestamp+type only if no stable
id exists) so items keep stable identity when latestEvents is prepended and you
can remove the timeout remount workaround; reference the useVirtualizer call
(rowVirtualizer) and the allEvents/latestEvents arrays when implementing this
change.

In `@src/components/custom/DrawerLatestRuns.tsx`:
- Around line 141-144: The code dereferences the resolved value of
getLatestRuns() (and similarly getMoreRuns()) without guarding against its
error-path fallback (which can resolve to []). Update the promise handlers for
getLatestRuns() and getMoreRuns() so they first check the resolved value and
provide a safe default (e.g., an object with data: [] and total: 0) before
calling setLogItems and setItemsTotal; alternatively, destructure with defaults
or early-return when data is falsy so the drawer falls back to the empty state
instead of crashing. Ensure you modify the promise then() callbacks where
setLogItems and setItemsTotal are invoked and handle both success shape and the
error fallback consistently.

In `@src/components/custom/general/LogFileList.tsx`:
- Line 36: The createAt value is being set using logFile.date?.toString(), which
yields a decimal string that moment() may misparse; update the code that assigns
createAt (used by FileItem) to pass a parse-safe ISO or numeric timestamp
instead—e.g., convert the numeric Unix timestamp to a Number or an ISO string
before assigning (use logFile.date as a Number or new
Date(logFile.date).toISOString()), so FileItem/moment() receives a reliably
parseable date; locate the createAt assignment in LogFileList and replace the
toString() usage accordingly.

In `@src/components/custom/general/MultiTypedInput.tsx`:
- Line 57: The change removed the default for the MultiTypedInput "type" prop
which unintentionally exposes the mode picker (see MultiTypedInput component
around the mode-picker logic at lines ~225-235) and allows "exact"/"regex"
values for fields like averageTime; to fix, restore the previous default type
behavior in MultiTypedInput (so the picker is hidden and "between"-style remains
the only selection) or update callers that expect only "between" (notably
advancedJobFilteringDialog's averageTime usage) to explicitly pass
type="between" or acceptedInputTypes={["between"]} so the UI cannot produce
invalid payloads.
- Around line 136-149: The handleRegexChange callback currently only reports
validation errors via onError when validateRegex(newValue) returns an error, but
never clears a previous error when the pattern becomes valid; update
handleRegexChange (and use validateRegex) so that after computing error you call
onError?.({ name }, error) when error exists and otherwise call onError?.({ name
}, undefined) (or null per your onError contract) to clear the stale error, then
proceed to setRegexValue and onChange as before.
- Around line 153-160: handleBetweenChange closes over the stale betweenValue;
change it to use a functional state update and remove betweenValue from the
dependency array: inside handleBetweenChange (the function defined with
useCallback) call setBetweenValue(prev => { const updated = { ...prev, [field]:
newValue, type: "between" }; onChange?.(updated); return updated; }) and keep
only onChange and setBetweenValue in the useCallback deps so the callback never
reads a stale betweenValue.
- Around line 102-117: handleTypeChange currently resets state and emits
payloads with the wrong shape when a controlled type prop is synced, and its
dependencies are incomplete; change the sync effect that reads prop `type` to
set `inputType` directly (do not call handleTypeChange on mount/prop-sync) so it
won’t reset values or emit mismatched payloads, and update handleTypeChange to
emit type-correct payloads (include the proper `type` field for exact/regex and
`{ type: "between", value1, value2 }` for between) when invoked by user actions;
also add the missing dependencies: include `name` in handleTypeChange, include
`betweenValue` in handleBetweenChange, and add `inputType` and
`handleTypeChange` to the effect that syncs controlled `type` prop so hooks
don’t use stale closures.

In `@src/components/custom/jobsTable/advancedJobFilteringDialog.tsx`:
- Around line 218-220: The current code returns the full FlexibleInput objects
(e.g., values.name, values.cronSetting, values.consumer) which contain {value,
type} instead of scalar strings, causing advancedFilters to propagate nested
objects into JobsService (affecting filterJobs and exportJobsToJSON); change the
assignments so they return the scalar text (e.g., use values.name?.value ?
values.name.value : undefined for name, and likewise for cronSetting and
consumer) so advancedFilters sends plain strings to the service.

In `@src/components/custom/notifications/NotificationServicesPanel.tsx`:
- Line 385: The details pane wrapper in NotificationServicesPanel (the div with
class "flex flex-col gap-2 overflow-y-auto") needs a bounded height so it
becomes the scroll container; update that element to include a height constraint
(e.g., add "h-full" or "min-h-0") in its class list so it doesn't auto-size and
the overflow-y works, and if the Spinner component wraps children with its own
container apply the same "min-h-0" constraint to the Spinner wrapper so the
scroll behavior remains contained.

In `@src/components/ui/date-picker-presets.tsx`:
- Line 147: The border classes are on the wrong element; remove "border-2
border-border" from the SelectValue className and add those classes to the
SelectTrigger className so the trigger renders the control chrome while
SelectValue keeps only text/placeholder styles (e.g., keep "bg-background
text-foreground" on SelectValue and ensure SelectTrigger receives "border-2
border-border" alongside its existing classes).

In `@src/components/ui/sidebar.tsx`:
- Line 225: The desktop sidebar's root element currently uses the class string
"group peer hidden md:block text-sidebar-foreground z-50" which conflicts with
modal/sheet/dropdown z-50 layers; update the sidebar's className in
src/components/ui/sidebar.tsx (the element with that exact string) to use a
lower z-index (e.g., z-40 or remove the z-* utility) so dialogs/sheets/menus
(which use z-50) deterministically render above the Sidebar.

In `@src/features/jobsTable/interfaces.tsx`:
- Around line 113-116: The flex container currently has the truncation class
which won't ellipsize because the flex item needs a shrinkable basis; update the
JSX so the outer div keeps "flex gap-2 text-[13px] items-center font-light"
(remove "truncate") and add "min-w-0" to that outer div, then move "truncate"
(and optionally "overflow-hidden" and "whitespace-nowrap" if used elsewhere) to
the consumer text node that renders row.original.consumer (the element alongside
<FileArchive />) so long consumer names properly ellipsize.

In `@src/features/jobsTable/jobsTable.tsx`:
- Around line 71-75: The empty-state branch currently applies min-h-[800px] when
data.length === 0 causing an oversized blank panel; change the conditional class
logic inside the component that builds className (the cn call using size and
data.length) to apply a bounded, compact empty-state height (for example replace
"min-h-[800px]" with a smaller bounded class such as "min-h-[160px] h-auto
max-h-[40vh] flex items-center justify-center" or similar design-system
empty-state classes) so the table stays within the viewport-bounded area and
visually shows a no-results state; make the same adjustment to the other
identical block that handles the empty rows (the second cn/className usage
around the lines referenced).

In `@src/hooks/useJobEvents.tsx`:
- Around line 122-125: The callbacks in useJobEvents.tsx (notably readEvents
plus the callbacks at the .finally block and the other two callbacks referenced)
close over a stale changeCounter; replace setChangeCounter(changeCounter + 1)
with the functional updater setChangeCounter(c => c + 1) in each location so
updates are always based on the latest state, and ensure readEvents and the
other callbacks no longer rely on changeCounter in their dependency arrays (use
the functional updater instead of adding changeCounter to deps); look for the
symbol readEvents and the three occurrences of setChangeCounter in the file and
update them to use the functional form.

---

Outside diff comments:
In `@src/components/custom/general/LogFileList.tsx`:
- Around line 28-46: The memoized FileList created by useMemo currently lists
[props.logFiles, props.readLogFile] but omits props.originName and
props.downloadLogFile, causing stale values when those props change; update the
useMemo dependency array for the FileList variable (the useMemo call that builds
FileItemProps) to include props.originName and props.downloadLogFile along with
props.logFiles and props.readLogFile so the memo recalculates when any of those
inputs change.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: a765adcb-6c53-4c4e-b723-c7a62adadd23

📥 Commits

Reviewing files that changed from the base of the PR and between 10e8378 and 00cb33e.

📒 Files selected for processing (15)
  • src/components/custom/DrawerJobEvents.tsx
  • src/components/custom/DrawerLatestRuns.tsx
  • src/components/custom/dashboard/statsBar.tsx
  • src/components/custom/general/LogFileList.tsx
  • src/components/custom/general/MultiTypedInput.tsx
  • src/components/custom/jobsTable/advancedJobFilteringDialog.tsx
  • src/components/custom/notifications/NotificationServicesPanel.tsx
  • src/components/ui/date-picker-presets.tsx
  • src/components/ui/sidebar.tsx
  • src/features/jobsTable/interfaces.tsx
  • src/features/jobsTable/jobsPage.tsx
  • src/features/jobsTable/jobsTable.tsx
  • src/hooks/useJobEvents.tsx
  • src/styles/global.css
  • tailwind.config.ts

Comment thread src/components/custom/dashboard/statsBar.tsx
Comment thread src/components/custom/dashboard/statsBar.tsx
Comment thread src/components/custom/DrawerJobEvents.tsx
Comment thread src/components/custom/DrawerLatestRuns.tsx Outdated
Comment thread src/components/custom/general/LogFileList.tsx Outdated
Comment thread src/components/ui/date-picker-presets.tsx Outdated
Comment thread src/components/ui/sidebar.tsx
Comment thread src/features/jobsTable/interfaces.tsx
Comment thread src/features/jobsTable/jobsTable.tsx
Comment thread src/hooks/useJobEvents.tsx
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/components/custom/jobsTable/advancedJobFilteringDialog.tsx (1)

301-308: ⚠️ Potential issue | 🟠 Major

FlexibleInput with type="regex" will reset pre-filled values on mount.

Setting type="regex" triggers the useEffect in MultiTypedInput.tsx (lines 162-166) which calls handleTypeChange, resetting all internal state and emitting an empty value. If the form is opened with existing filter values, they will be silently wiped.

This is a downstream effect of the issue in MultiTypedInput.tsx. Once the root cause is fixed there (by syncing inputType directly instead of calling handleTypeChange), this usage will work correctly.

Also applies to: 321-328, 341-348

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/custom/jobsTable/advancedJobFilteringDialog.tsx` around lines
301 - 308, The FlexibleInput usages (e.g., the instance with type="regex" in
advancedJobFilteringDialog) are getting their pre-filled values wiped because
MultiTypedInput.tsx's useEffect invokes handleTypeChange (lines around 162-166),
which resets internal state and emits an empty value; fix MultiTypedInput by
syncing the internal inputType state directly from props (set inputType =
props.type or similar) in that effect instead of calling handleTypeChange so you
don't reset other internal state or call the value-emitting logic; ensure
handleTypeChange remains the explicit user-triggered transition and that
existing field.value is preserved and not overwritten when the component mounts.
src/features/jobsTable/jobsTable.tsx (1)

78-102: ⚠️ Potential issue | 🟠 Major

Remove w-full from body cells or replace flex-grow with flex-shrink-0 in both headers and body to enforce strict column sizing.

Body cells (lines 185-187) have conflicting width constraints: w-full conflicts with the inline style={{ width: cell.column.getSize() }}. Headers lack w-full, creating inconsistency. Combined with mixed column sizing (some columns use fixed size, others use only minSize), the flex-grow property allows flex-basis to override explicit widths, causing subtle misalignment.

Remove w-full from body cells to match headers, or use flex-shrink-0 in both to enforce strict sizing without proportional expansion.

Also applies to: 181-193

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/features/jobsTable/jobsTable.tsx` around lines 78 - 102, Table cells are
misaligning because body cells use the tailwind class "w-full" while headers use
"flex flex-grow" and widths are set via
header.column.getSize()/cell.column.getSize(), causing flex-basis to be
overridden; remove "w-full" from the body cell elements (the TableCell elements
that use style={{ width: cell.column.getSize() }}) so they mirror the TableHead
layout, or alternatively change both header and body containers from "flex-grow"
to "flex-shrink-0" (update classNames on TableRow/TableHead and the
corresponding TableCell) to enforce strict sizing based on column.getSize().
♻️ Duplicate comments (7)
src/components/custom/general/MultiTypedInput.tsx (4)

112-117: ⚠️ Potential issue | 🟠 Major

Emitted payloads have incorrect type field.

When newType is "regex", line 114 emits defaultMultiTypeNullValue which hardcodes type: "exact" (see line 28). The emitted object's type should match newType.

Suggested fix
      // Emit initial value for new type
      if (newType === "exact" || newType === "regex") {
-       onChange?.(defaultMultiTypeNullValue)
+       onChange?.({ value: "", type: newType })
      } else if (newType === "between") {
-       onChange?.({ value1: "", value2: "" })
+       onChange?.({ value1: "", value2: "", type: "between" })
      }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/custom/general/MultiTypedInput.tsx` around lines 112 - 117,
The emitted payload uses defaultMultiTypeNullValue which hardcodes type:"exact",
so when newType is "regex" (or other types) the emitted object's type is wrong;
update the emission logic in the onChange block (the code referencing newType,
defaultMultiTypeNullValue, and onChange) to emit an object whose type field
equals newType (e.g., construct { type: newType, value: ... } or clone
defaultMultiTypeNullValue but set type: newType) and for "between" continue to
emit { value1: "", value2: "" } as before.

162-166: ⚠️ Potential issue | 🔴 Critical

Controlled type prop causes value reset on mount.

When a parent passes type="regex" along with a pre-filled value (as in advancedJobFilteringDialog.tsx lines 301-308), this useEffect fires on mount and calls handleTypeChange(type). That handler resets all internal state (lines 107-109) and emits an empty value via onChange, silently wiping the pre-filled field.value.

Synchronize inputType directly without invoking the reset logic:

Suggested fix
  useEffect(() => {
-   if (type) {
-     handleTypeChange(type)
+   if (type && type !== inputType) {
+     setInputType(type)
    }
- }, [type, handleTypeChange])
+ }, [type, inputType])
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/custom/general/MultiTypedInput.tsx` around lines 162 - 166,
The useEffect currently calls handleTypeChange(type) on mount which triggers the
reset logic (clearing internal state and calling onChange); instead, update the
internal inputType state directly without invoking reset/emit: in the component
MultiTypedInput, replace calling handleTypeChange from the useEffect with a
direct state sync (e.g., setInputType(type) or the existing inputType setter) so
the controlled prop synchronizes the UI but does not call handleTypeChange or
emit an empty value; alternatively, add an optional flag parameter to
handleTypeChange (e.g., handleTypeChange(type, { preserveValue: true })) and
call that from the useEffect to avoid clearing value and calling onChange.

153-160: ⚠️ Potential issue | 🔴 Critical

Stale closure on betweenValue causes lost edits.

handleBetweenChange spreads betweenValue (line 155) but the dependency array includes it, creating a new callback on every state change. Worse, if React batches updates, the closure can still reference stale state. Use functional setState to avoid the stale-closure bug entirely.

Suggested fix
  const handleBetweenChange = useCallback(
    (field: keyof BetweenValue, newValue: string) => {
-     const updated = { ...betweenValue, [field]: newValue, type: "between" }
-     setBetweenValue(updated)
-     onChange?.(updated)
+     setBetweenValue(prev => {
+       const updated = { ...prev, [field]: newValue, type: "between" }
+       onChange?.(updated)
+       return updated
+     })
    },
-   [onChange, setBetweenValue, betweenValue],
+   [onChange],
  )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/custom/general/MultiTypedInput.tsx` around lines 153 - 160,
The handleBetweenChange callback closes over betweenValue causing stale updates;
change it to use the functional form of setBetweenValue so you compute the new
state from the previous value (e.g. setBetweenValue(prev => ({ ...prev, [field]:
newValue, type: "between" }))) and call onChange with that computed updated
object; then remove betweenValue from the useCallback dependency array so the
callback only depends on onChange and setBetweenValue (reference:
handleBetweenChange, setBetweenValue, betweenValue, onChange).

143-147: ⚠️ Potential issue | 🟠 Major

Stale regex error persists after pattern is corrected.

onError is only called when validateRegex returns an error. Once the user fixes the pattern, the previous error is never cleared.

Suggested fix
      const error = validateRegex(newValue)
-     if (error) {
-       onError?.({ name }, error)
-     }
+     onError?.({ name }, error) // clears when error is undefined
      onChange?.(typedValue)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/custom/general/MultiTypedInput.tsx` around lines 143 - 147,
validateRegex is only used to call onError when an error exists, so once the
user corrects the pattern the previous error remains; after computing error =
validateRegex(newValue) call onError with a cleared error when no error is
present (e.g., onError?.({ name }, undefined) or null) before calling onChange
so that the parent clears validation state; update the block around
validateRegex/newValue/typedValue to explicitly clear the error when
validateRegex returns falsy.
src/components/custom/jobsTable/advancedJobFilteringDialog.tsx (1)

218-220: ⚠️ Potential issue | 🟠 Major

Verify backend API contract for filter payload shape.

These lines send the full { value, type } objects to the backend. Per JobsService.ts:25-34, filterJobs spreads advancedFilters directly into the POST body. If the backend expects scalar strings for name/cronSetting/consumer, this breaks filtering; if it expects the object shape to distinguish regex vs exact matching, this is correct.

#!/bin/bash
# Check how the backend handles these filter fields
# Look for filter handling in backend or API types

# Search for filterJobs endpoint handling or types
rg -n -C5 'filterJobs|advancedFilters' --type ts --type js

# Check if there are any API type definitions
fd -t f -i 'api' -e ts -e d.ts --exec cat {}

If the backend expects plain strings, apply this fix:

Suggested fix for scalar values
-       name: values.name?.value ? values.name : undefined,
-       cronSetting: values.cronSetting?.value ? values.cronSetting : undefined,
-       consumer: values.consumer?.value ? values.consumer : undefined,
+       name: values.name?.value ? values.name.value : undefined,
+       cronSetting: values.cronSetting?.value ? values.cronSetting.value : undefined,
+       consumer: values.consumer?.value ? values.consumer.value : undefined,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/custom/jobsTable/advancedJobFilteringDialog.tsx` around lines
218 - 220, The code currently sends full {value,type} objects for
name/cronSetting/consumer from advancedJobFilteringDialog; verify the backend
contract in JobsService.filterJobs (see JobsService.ts lines around 25-34) to
confirm whether advancedFilters should contain scalar strings or the
{value,type} shape; if the backend expects scalars, change the payload
construction in advancedJobFilteringDialog so name: values.name?.value ?
values.name.value : undefined (and likewise for cronSetting and consumer); if
the backend expects the object shape, leave as-is but add/type the
advancedFilters payload to reflect the {value,type} structure so filterJobs and
any API types match.
src/components/custom/notifications/NotificationServicesPanel.tsx (1)

385-385: ⚠️ Potential issue | 🟠 Major

Right details pane still lacks a bounded scroll container.

Line 385 is still an unconstrained column wrapper, so in short viewports this section can grow/clamp under the parent overflow-hidden container instead of scrolling internally.

Suggested fix
-                <div className="flex flex-col gap-2">
+                <div className="flex h-full min-h-0 flex-col gap-2 overflow-y-auto">
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/custom/notifications/NotificationServicesPanel.tsx` at line
385, The right details pane in NotificationServicesPanel uses an unconstrained
<div className="flex flex-col gap-2"> which allows content to grow past the
parent overflow-hidden instead of scrolling; update that element (or its
immediate wrapper in NotificationServicesPanel) to be a bounded, scrollable
container by adding sizing and scroll utility classes such as: include min-h-0
and either flex-1 or a max-h value plus overflow-auto (e.g. className="flex
flex-col gap-2 flex-1 min-h-0 overflow-auto") so the column can shrink and
scroll correctly inside the parent.
src/features/jobsTable/jobsTable.tsx (1)

71-75: ⚠️ Potential issue | 🟡 Minor

Remove min-h-[800px] for empty state—already handled in TableBodyVirtualized.

The empty state is properly rendered in TableBodyVirtualized (lines 141-146) with a "No results" message. The min-h-[800px] on the wrapper creates an unnecessarily large blank panel when data is empty, conflicting with the viewport-bounded height passed via size.

🔧 Suggested fix
       className={cn(
         "rounded-md border border-border w-full overflow-auto relative",
         size,
-        data.length > 0 ? "" : "min-h-[800px]",
       )}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/features/jobsTable/jobsTable.tsx` around lines 71 - 75, The wrapper div
in jobsTable.tsx currently injects "min-h-[800px]" when data is empty, causing a
large blank area; remove that fallback from the className expression so the
wrapper only uses cn("rounded-md border border-border w-full overflow-auto
relative", size) and let TableBodyVirtualized handle the empty state rendering
(refer to TableBodyVirtualized and the size and data variables used in the
className logic).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/components/custom/dashboard/statsBar.tsx`:
- Around line 79-86: The Tooltip instance (Tooltip, TooltipTrigger,
TooltipContent) needs to be wrapped with Radix's TooltipProvider to provide
context; import TooltipProvider and wrap the existing Tooltip block (the
component using FileWarningIcon and tooltipContent) with <TooltipProvider> so
the provider is an ancestor of Tooltip; ensure the import list for Tooltip
includes TooltipProvider and that any props required by TooltipProvider (if used
elsewhere) are passed through.
- Around line 57-63: The nested ternary mapping of the textSize prop in the
className (inside the StatsBar component in statsBar.tsx) is redundant because
textSize already contains the Tailwind class string; replace the ternary logic
in the cn(...) call to include textSize directly (e.g., pass textSize) and
provide a fallback default like "text-2xl" when textSize is falsy to preserve
current behavior; update any references to the textSize prop usage to rely on
the direct class string instead of remapping.

In `@src/components/custom/general/MultiTypedInput.tsx`:
- Line 119: The dependency arrays in MultiTypedInput's hooks include stable
React state setters (setExactValue, setRegexValue, setBetweenValue)
unnecessarily; remove these setters from the dependency arrays of the affected
callbacks/effects (the hooks referencing onChange, onError, name) so only actual
changing deps (e.g., onChange, onError, name) remain—apply the same removal to
the other two occurrences in the file that match the pattern.

In `@src/features/jobsTable/jobsPage.tsx`:
- Line 271: The sentinel div with ref (the line <div ref={ref}
className="h-[1px]"></div>) is used with useInView but useInView is defaulting
to the viewport, so when the table has a fixed internal scroll
(size="h-[calc(100vh-150px)]") the sentinel stays visible and the sticky header
never toggles; update the useInView call to supply the table scrolling container
as the root option (i.e., pass the element reference for the table
container/wrapper used for the fixed-height scroll) so the intersection observer
is scoped to that container, or alternatively remove/adjust the sentinel and
sticky styling if sticky behavior is no longer desired.

In `@src/features/jobsTable/jobsTable.tsx`:
- Line 176: The className string in the JSX element in jobsTable.tsx contains a
leading space (" flex absolute w-full border-b-border"); remove the leading
space so the attribute reads "flex absolute w-full border-b-border". Locate the
JSX element with className=" flex absolute w-full border-b-border" and update
the string to drop the initial space to match other class definitions.
- Around line 126-132: The overscan for the virtualized table is set very high
in the rowVirtualizer configuration (useVirtualizer call) which defeats
virtualization benefits; change the overscan value from 42 to a much smaller,
typical number (e.g., 5-10—try 8) in the options passed to useVirtualizer to
reduce offscreen rendering while keeping smooth scroll behavior.
- Line 49: The ref tableContainerRef is initialized with useRef(null) which
infers RefObject<null>; update its initialization to explicitly type it as
React.RefObject<HTMLDivElement> (or useRef<HTMLDivElement | null>(null)) so it
matches the TableBodyProps typing; change the declaration of tableContainerRef
to useRef<HTMLDivElement | null>(null) (or the equivalent
React.RefObject<HTMLDivElement>) to ensure type safety across the component.

---

Outside diff comments:
In `@src/components/custom/jobsTable/advancedJobFilteringDialog.tsx`:
- Around line 301-308: The FlexibleInput usages (e.g., the instance with
type="regex" in advancedJobFilteringDialog) are getting their pre-filled values
wiped because MultiTypedInput.tsx's useEffect invokes handleTypeChange (lines
around 162-166), which resets internal state and emits an empty value; fix
MultiTypedInput by syncing the internal inputType state directly from props (set
inputType = props.type or similar) in that effect instead of calling
handleTypeChange so you don't reset other internal state or call the
value-emitting logic; ensure handleTypeChange remains the explicit
user-triggered transition and that existing field.value is preserved and not
overwritten when the component mounts.

In `@src/features/jobsTable/jobsTable.tsx`:
- Around line 78-102: Table cells are misaligning because body cells use the
tailwind class "w-full" while headers use "flex flex-grow" and widths are set
via header.column.getSize()/cell.column.getSize(), causing flex-basis to be
overridden; remove "w-full" from the body cell elements (the TableCell elements
that use style={{ width: cell.column.getSize() }}) so they mirror the TableHead
layout, or alternatively change both header and body containers from "flex-grow"
to "flex-shrink-0" (update classNames on TableRow/TableHead and the
corresponding TableCell) to enforce strict sizing based on column.getSize().

---

Duplicate comments:
In `@src/components/custom/general/MultiTypedInput.tsx`:
- Around line 112-117: The emitted payload uses defaultMultiTypeNullValue which
hardcodes type:"exact", so when newType is "regex" (or other types) the emitted
object's type is wrong; update the emission logic in the onChange block (the
code referencing newType, defaultMultiTypeNullValue, and onChange) to emit an
object whose type field equals newType (e.g., construct { type: newType, value:
... } or clone defaultMultiTypeNullValue but set type: newType) and for
"between" continue to emit { value1: "", value2: "" } as before.
- Around line 162-166: The useEffect currently calls handleTypeChange(type) on
mount which triggers the reset logic (clearing internal state and calling
onChange); instead, update the internal inputType state directly without
invoking reset/emit: in the component MultiTypedInput, replace calling
handleTypeChange from the useEffect with a direct state sync (e.g.,
setInputType(type) or the existing inputType setter) so the controlled prop
synchronizes the UI but does not call handleTypeChange or emit an empty value;
alternatively, add an optional flag parameter to handleTypeChange (e.g.,
handleTypeChange(type, { preserveValue: true })) and call that from the
useEffect to avoid clearing value and calling onChange.
- Around line 153-160: The handleBetweenChange callback closes over betweenValue
causing stale updates; change it to use the functional form of setBetweenValue
so you compute the new state from the previous value (e.g. setBetweenValue(prev
=> ({ ...prev, [field]: newValue, type: "between" }))) and call onChange with
that computed updated object; then remove betweenValue from the useCallback
dependency array so the callback only depends on onChange and setBetweenValue
(reference: handleBetweenChange, setBetweenValue, betweenValue, onChange).
- Around line 143-147: validateRegex is only used to call onError when an error
exists, so once the user corrects the pattern the previous error remains; after
computing error = validateRegex(newValue) call onError with a cleared error when
no error is present (e.g., onError?.({ name }, undefined) or null) before
calling onChange so that the parent clears validation state; update the block
around validateRegex/newValue/typedValue to explicitly clear the error when
validateRegex returns falsy.

In `@src/components/custom/jobsTable/advancedJobFilteringDialog.tsx`:
- Around line 218-220: The code currently sends full {value,type} objects for
name/cronSetting/consumer from advancedJobFilteringDialog; verify the backend
contract in JobsService.filterJobs (see JobsService.ts lines around 25-34) to
confirm whether advancedFilters should contain scalar strings or the
{value,type} shape; if the backend expects scalars, change the payload
construction in advancedJobFilteringDialog so name: values.name?.value ?
values.name.value : undefined (and likewise for cronSetting and consumer); if
the backend expects the object shape, leave as-is but add/type the
advancedFilters payload to reflect the {value,type} structure so filterJobs and
any API types match.

In `@src/components/custom/notifications/NotificationServicesPanel.tsx`:
- Line 385: The right details pane in NotificationServicesPanel uses an
unconstrained <div className="flex flex-col gap-2"> which allows content to grow
past the parent overflow-hidden instead of scrolling; update that element (or
its immediate wrapper in NotificationServicesPanel) to be a bounded, scrollable
container by adding sizing and scroll utility classes such as: include min-h-0
and either flex-1 or a max-h value plus overflow-auto (e.g. className="flex
flex-col gap-2 flex-1 min-h-0 overflow-auto") so the column can shrink and
scroll correctly inside the parent.

In `@src/features/jobsTable/jobsTable.tsx`:
- Around line 71-75: The wrapper div in jobsTable.tsx currently injects
"min-h-[800px]" when data is empty, causing a large blank area; remove that
fallback from the className expression so the wrapper only uses cn("rounded-md
border border-border w-full overflow-auto relative", size) and let
TableBodyVirtualized handle the empty state rendering (refer to
TableBodyVirtualized and the size and data variables used in the className
logic).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: fbd1c300-2100-4501-bd5b-502d5c08cd11

📥 Commits

Reviewing files that changed from the base of the PR and between 00cb33e and 66f38af.

📒 Files selected for processing (8)
  • src/components/custom/dashboard/statsBar.tsx
  • src/components/custom/general/MultiTypedInput.tsx
  • src/components/custom/jobsTable/advancedJobFilteringDialog.tsx
  • src/components/custom/notifications/NotificationServicesPanel.tsx
  • src/components/ui/date-picker-presets.tsx
  • src/features/jobsTable/jobsPage.tsx
  • src/features/jobsTable/jobsTable.tsx
  • src/features/system/database.tsx

Comment thread src/components/custom/dashboard/statsBar.tsx Outdated
Comment thread src/components/custom/dashboard/statsBar.tsx Outdated
Comment thread src/components/custom/general/MultiTypedInput.tsx
Comment thread src/features/jobsTable/jobsPage.tsx
Comment thread src/features/jobsTable/jobsTable.tsx
Comment thread src/features/jobsTable/jobsTable.tsx
Comment thread src/features/jobsTable/jobsTable.tsx Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
src/components/custom/dashboard/statsBar.tsx (1)

7-11: ⚠️ Potential issue | 🔴 Critical

TooltipProvider is used but not imported — runtime error.

Line 75 uses <TooltipProvider> but it's missing from the import. This will cause a ReferenceError when rendering the error state.

🐛 Proposed fix
 import {
   Tooltip,
   TooltipContent,
   TooltipTrigger,
+  TooltipProvider,
 } from "@/components/ui/tooltip"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/custom/dashboard/statsBar.tsx` around lines 7 - 11, The
component is using TooltipProvider (rendered at line ~75) but it's not imported,
causing a runtime ReferenceError; fix by adding TooltipProvider to the import
list alongside Tooltip, TooltipContent, TooltipTrigger from
"@/components/ui/tooltip" so the symbol is available when rendering the error
state (update the import statement that currently imports Tooltip,
TooltipContent, TooltipTrigger to also import TooltipProvider).
src/components/custom/general/LogFileList.tsx (2)

61-68: ⚠️ Potential issue | 🟡 Minor

Missing key prop on virtualized list items.

Each FileItem rendered in the .map() lacks a key prop. This causes React reconciliation issues—especially problematic in virtualized lists where elements are reused. Use virtualRow.index or a unique identifier like fileId.

🔧 Proposed fix
         {rowVirtualizer.getVirtualItems().map((virtualRow, index) => (
           <FileItem
+            key={virtualRow.index}
             className="mb-2 absolute"
             style={{
               transform: `translateY(${virtualRow.start}px)`,
             }}
             {...FileList[virtualRow?.index ?? index]}
           />
         ))}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/custom/general/LogFileList.tsx` around lines 61 - 68, The
FileItem elements rendered from rowVirtualizer.getVirtualItems() are missing a
React key; update the map so each FileItem receives a stable key (use
virtualRow.index or a unique identifier from the data). For example, inside the
mapping that spreads {...FileList[virtualRow?.index ?? index]}, add a key prop
referencing virtualRow.index or FileList[virtualRow.index].fileId (fall back to
virtualRow.index if no fileId) to ensure proper reconciliation for the FileItem
components.

48-48: ⚠️ Potential issue | 🟡 Minor

Missing props.downloadLogFile in useMemo dependency array.

props.downloadLogFile is captured on line 45 but not listed in the dependencies. If this prop changes, FileList won't re-compute, leading to stale callback references.

🔧 Proposed fix
-  }, [props.logFiles, props.readLogFile])
+  }, [props.logFiles, props.readLogFile, props.downloadLogFile, props.originName])

Note: props.originName (line 39) is also missing from the dependency array.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/custom/general/LogFileList.tsx` at line 48, The useMemo that
builds FileList is missing dependencies: add props.downloadLogFile and
props.originName to the dependency array alongside props.logFiles and
props.readLogFile so the memo recomputes when those callbacks/props change;
update the dependency list used in the useMemo call within the
FileList-producing block to include downloadLogFile and originName to avoid
stale references.
♻️ Duplicate comments (6)
src/components/custom/DrawerJobEvents.tsx (2)

83-83: ⚠️ Potential issue | 🟡 Minor

Add fallback for potentially undefined items in getItemKey.

If allEvents[index] is undefined during a render-update race, this will throw. The previous review suggested adding a fallback which appears to have been partially addressed but the safety guard is missing.

🛡️ Suggested fix
-    getItemKey: index => String(allEvents[index].id),
+    getItemKey: index => String(allEvents[index]?.id ?? index),
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/custom/DrawerJobEvents.tsx` at line 83, The getItemKey
function can throw if allEvents[index] is undefined during render; update
getItemKey (in the DrawerJobEvents component) to guard against missing items and
return a stable fallback key, e.g. use the event id when present and otherwise
fall back to the index or a prefixed index string (e.g. use allEvents[index]?.id
?? index), ensuring the returned value is always a string.

144-149: 🧹 Nitpick | 🔵 Trivial

key={allEvents.length} forces unnecessary remounts.

Using allEvents.length as the key on the container div forces React to unmount and remount the entire virtualized list whenever items are added or removed. This defeats some benefits of virtualization and may cause scroll position resets.

Consider removing this key or using a more stable identifier if the goal is to trigger re-measurement. The refreshVirtualizer callback with rowVirtualizer.measure() should handle updates.

♻️ Proposed fix
           <div
             className="relative w-full py-4"
             style={{
               height: `${rowVirtualizer.getTotalSize()}px`,
             }}
-            key={allEvents.length}
           >
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/custom/DrawerJobEvents.tsx` around lines 144 - 149, The
container div is using key={allEvents.length}, which forces full remounts of the
virtualized list; remove that unstable key (or replace it with a stable
identifier if you truly need remounting) and rely on the virtualizer re-measure
flow instead—call refreshVirtualizer / rowVirtualizer.measure() after updates so
the list recomputes sizes rather than forcing React to unmount/remount the
element; update the JSX around the div in DrawerJobEvents.tsx to drop the
length-based key and ensure any existing refreshVirtualizer usage invokes
rowVirtualizer.measure() when items change.
src/components/custom/general/MultiTypedInput.tsx (4)

155-162: 🛠️ Refactor suggestion | 🟠 Major

Callback recreated on every value change; prefer functional setState.

Adding betweenValue to the dependency array fixes the stale closure but defeats memoization—the callback is recreated on every keystroke. Using functional state update avoids the closure issue while keeping a stable callback identity.

Proposed fix using functional setState
   const handleBetweenChange = useCallback(
     (field: keyof BetweenValue, newValue: string) => {
-      const updated = { ...betweenValue, [field]: newValue, type: "between" }
-      setBetweenValue(updated)
-      onChange?.(updated)
+      setBetweenValue(prev => {
+        const updated = { ...prev, [field]: newValue, type: "between" }
+        onChange?.(updated)
+        return updated
+      })
     },
-    [onChange, setBetweenValue, betweenValue],
+    [onChange],
   )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/custom/general/MultiTypedInput.tsx` around lines 155 - 162,
The handleBetweenChange callback closes over betweenValue causing either stale
state or recreation on every keystroke; change it to use the functional form of
setBetweenValue so the callback can omit betweenValue from its deps and remain
stable: inside handleBetweenChange call setBetweenValue(prev => { const updated
= { ...prev, [field]: newValue, type: "between" }; onChange?.(updated); return
updated; }); then update the dependency array to only include onChange (and any
other stable refs) so handleBetweenChange remains memoized; reference
functions/values: handleBetweenChange, setBetweenValue, onChange, BetweenValue.

113-114: ⚠️ Potential issue | 🔴 Critical

Emitted payload has incorrect type field for regex mode.

When newType === "regex", the code emits defaultMultiTypeNullValue which is hardcoded to { value: "", type: "exact" }. The emitted value should reflect the actual selected type.

Proposed fix
       // Emit initial value for new type
       if (newType === "exact" || newType === "regex") {
-        onChange?.(defaultMultiTypeNullValue)
+        onChange?.({ value: "", type: newType })
       } else if (newType === "between") {
-        onChange?.({ value1: "", value2: "" })
+        onChange?.({ value1: "", value2: "", type: "between" })
       }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/custom/general/MultiTypedInput.tsx` around lines 113 - 114,
The emitted payload uses defaultMultiTypeNullValue which hardcodes type:"exact",
so when newType === "regex" the emitted value has the wrong type; update the
logic in the MultiTypedInput component where newType is handled (the block that
calls onChange?.(defaultMultiTypeNullValue)) to emit a value whose type matches
newType (e.g., clone/construct the default payload but set type: newType) so
onChange receives the correct type for "regex" and "exact".

164-168: ⚠️ Potential issue | 🔴 Critical

Effect resets values on mount when controlled type is provided.

Calling handleTypeChange(type) in this effect resets all internal state and emits empty payloads. On mount, if both type and value props are provided, the value initialization effect (lines 86-99) runs, then this effect fires and clears everything. The effect should only synchronize inputType state without triggering the reset logic.

Proposed fix
   useEffect(() => {
-    if (type) {
-      handleTypeChange(type)
+    if (type && type !== inputType) {
+      setInputType(type)
     }
-  }, [type, handleTypeChange])
+  }, [type, inputType])
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/custom/general/MultiTypedInput.tsx` around lines 164 - 168,
The effect currently calls handleTypeChange(type) on mount which triggers full
reset logic; instead only synchronize the local inputType state when a
controlled type prop is provided. Replace the call to handleTypeChange(type) in
the useEffect with logic that sets the internal inputType (e.g.
setInputType(type) or equivalent) and avoid invoking handleTypeChange so the
value initialization effect (value prop handling) isn't clobbered by the reset;
ensure you still react to actual type changes by invoking handleTypeChange only
when a real user-driven change is intended.

136-152: ⚠️ Potential issue | 🟡 Minor

Missing name in dependency array causes stale closure.

The callback references name at lines 145 and 147 but the dependency array at line 151 only includes [onChange, onError]. If the name prop changes, the callback will use the stale value.

Proposed fix
     [onChange, onError],
+    [name, onChange, onError],
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/custom/general/MultiTypedInput.tsx` around lines 136 - 152,
The handleRegexChange callback captures the prop name but it's not declared in
the dependency array, causing a stale closure; update the useCallback dependency
array for handleRegexChange to include name (in addition to onChange and
onError) so the closure sees updated name values, and ensure related symbols
(handleRegexChange, setRegexValue, validateRegex, onError, onChange, name) are
referenced correctly when updating the dependency list.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/components/custom/DrawerJobEvents.tsx`:
- Around line 86-92: The workaround using setTimeout in refreshVirtualizer and
the remount via key={allEvents.length} causes unreliable virtualizer updates;
remove the key={allEvents.length} and replace the timeout-based refresh by
adding a useEffect that watches allEvents (or its length/identity) and calls
rowVirtualizer.measure() when it changes (while keeping refreshVirtualizer for
manual triggers if needed); ensure you reference rowVirtualizer.measure and the
allEvents state inside the effect and clean up any stale closures by including
rowVirtualizer in the effect dependency array.
- Line 19: Remove the unused import "debounce" from the top of the
DrawerJobEvents component file; locate the import line that reads import {
debounce } from "@/utils/generalUtils" and delete it (or remove debounce from
the named imports if combined), ensuring the DrawerJobEvents component and any
other imports remain unchanged.
- Around line 70-72: The useMemo for allEvents includes redundant dependencies
(latestEvents.length and events.length); update the dependency array to only
include latestEvents and events so it becomes useMemo(() => [...latestEvents,
...events], [latestEvents, events]) — modify the dependency list around the
allEvents declaration to remove the .length entries while keeping the useMemo
logic unchanged.

In `@src/features/jobsTable/jobsTable.tsx`:
- Around line 170-195: The TableRow inside TableBodyRow has a redundant key prop
(key={row.id}) because the parent map already sets the key; remove the inner key
attribute from the TableRow in the TableBodyRow component (leave data-index,
ref, className, style, and children unchanged) so there’s a single key provided
by the parent mapping over rows.
- Around line 137-146: The TableBody is given a fixed height from
rowVirtualizer.getTotalSize(), which is 0 when rows.length === 0 and causes the
"No results." TableRow (h-24) to be clipped; update the rendering in
jobsTable.tsx so the style for TableBody only sets height when rows exist (e.g.,
if rows.length > 0 use height: `${rowVirtualizer.getTotalSize()}px`) or
alternatively apply a minHeight (e.g., minHeight: '6rem') when rows are empty;
locate the TableBody usage that references rowVirtualizer.getTotalSize() and
adjust the conditional styling to ensure the empty-state TableRow is visible.

---

Outside diff comments:
In `@src/components/custom/dashboard/statsBar.tsx`:
- Around line 7-11: The component is using TooltipProvider (rendered at line
~75) but it's not imported, causing a runtime ReferenceError; fix by adding
TooltipProvider to the import list alongside Tooltip, TooltipContent,
TooltipTrigger from "@/components/ui/tooltip" so the symbol is available when
rendering the error state (update the import statement that currently imports
Tooltip, TooltipContent, TooltipTrigger to also import TooltipProvider).

In `@src/components/custom/general/LogFileList.tsx`:
- Around line 61-68: The FileItem elements rendered from
rowVirtualizer.getVirtualItems() are missing a React key; update the map so each
FileItem receives a stable key (use virtualRow.index or a unique identifier from
the data). For example, inside the mapping that spreads
{...FileList[virtualRow?.index ?? index]}, add a key prop referencing
virtualRow.index or FileList[virtualRow.index].fileId (fall back to
virtualRow.index if no fileId) to ensure proper reconciliation for the FileItem
components.
- Line 48: The useMemo that builds FileList is missing dependencies: add
props.downloadLogFile and props.originName to the dependency array alongside
props.logFiles and props.readLogFile so the memo recomputes when those
callbacks/props change; update the dependency list used in the useMemo call
within the FileList-producing block to include downloadLogFile and originName to
avoid stale references.

---

Duplicate comments:
In `@src/components/custom/DrawerJobEvents.tsx`:
- Line 83: The getItemKey function can throw if allEvents[index] is undefined
during render; update getItemKey (in the DrawerJobEvents component) to guard
against missing items and return a stable fallback key, e.g. use the event id
when present and otherwise fall back to the index or a prefixed index string
(e.g. use allEvents[index]?.id ?? index), ensuring the returned value is always
a string.
- Around line 144-149: The container div is using key={allEvents.length}, which
forces full remounts of the virtualized list; remove that unstable key (or
replace it with a stable identifier if you truly need remounting) and rely on
the virtualizer re-measure flow instead—call refreshVirtualizer /
rowVirtualizer.measure() after updates so the list recomputes sizes rather than
forcing React to unmount/remount the element; update the JSX around the div in
DrawerJobEvents.tsx to drop the length-based key and ensure any existing
refreshVirtualizer usage invokes rowVirtualizer.measure() when items change.

In `@src/components/custom/general/MultiTypedInput.tsx`:
- Around line 155-162: The handleBetweenChange callback closes over betweenValue
causing either stale state or recreation on every keystroke; change it to use
the functional form of setBetweenValue so the callback can omit betweenValue
from its deps and remain stable: inside handleBetweenChange call
setBetweenValue(prev => { const updated = { ...prev, [field]: newValue, type:
"between" }; onChange?.(updated); return updated; }); then update the dependency
array to only include onChange (and any other stable refs) so
handleBetweenChange remains memoized; reference functions/values:
handleBetweenChange, setBetweenValue, onChange, BetweenValue.
- Around line 113-114: The emitted payload uses defaultMultiTypeNullValue which
hardcodes type:"exact", so when newType === "regex" the emitted value has the
wrong type; update the logic in the MultiTypedInput component where newType is
handled (the block that calls onChange?.(defaultMultiTypeNullValue)) to emit a
value whose type matches newType (e.g., clone/construct the default payload but
set type: newType) so onChange receives the correct type for "regex" and
"exact".
- Around line 164-168: The effect currently calls handleTypeChange(type) on
mount which triggers full reset logic; instead only synchronize the local
inputType state when a controlled type prop is provided. Replace the call to
handleTypeChange(type) in the useEffect with logic that sets the internal
inputType (e.g. setInputType(type) or equivalent) and avoid invoking
handleTypeChange so the value initialization effect (value prop handling) isn't
clobbered by the reset; ensure you still react to actual type changes by
invoking handleTypeChange only when a real user-driven change is intended.
- Around line 136-152: The handleRegexChange callback captures the prop name but
it's not declared in the dependency array, causing a stale closure; update the
useCallback dependency array for handleRegexChange to include name (in addition
to onChange and onError) so the closure sees updated name values, and ensure
related symbols (handleRegexChange, setRegexValue, validateRegex, onError,
onChange, name) are referenced correctly when updating the dependency list.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: a819b92c-6b92-44ea-bbd7-02dcef828a68

📥 Commits

Reviewing files that changed from the base of the PR and between 66f38af and 62c525a.

📒 Files selected for processing (6)
  • src/components/custom/DrawerJobEvents.tsx
  • src/components/custom/DrawerLatestRuns.tsx
  • src/components/custom/dashboard/statsBar.tsx
  • src/components/custom/general/LogFileList.tsx
  • src/components/custom/general/MultiTypedInput.tsx
  • src/features/jobsTable/jobsTable.tsx

import { ListCheck } from "lucide-react"
import { useVirtualizer } from "@tanstack/react-virtual"
import LoadingOverlay from "@/components/custom/LoadingOverlay"
import { debounce } from "@/utils/generalUtils"
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Unused import: debounce.

The debounce function is imported but never used in this file. This appears to be leftover from development.

🧹 Proposed fix
 import LoadingOverlay from "@/components/custom/LoadingOverlay"
-import { debounce } from "@/utils/generalUtils"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import { debounce } from "@/utils/generalUtils"
import LoadingOverlay from "@/components/custom/LoadingOverlay"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/custom/DrawerJobEvents.tsx` at line 19, Remove the unused
import "debounce" from the top of the DrawerJobEvents component file; locate the
import line that reads import { debounce } from "@/utils/generalUtils" and
delete it (or remove debounce from the named imports if combined), ensuring the
DrawerJobEvents component and any other imports remain unchanged.

Comment on lines +70 to +72
const allEvents = useMemo(() => {
return [...latestEvents, ...events]
}, [events, latestEvents, latestEvents.length, events.length])
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Redundant dependency array entries.

The .length properties are redundant since events and latestEvents references change when the arrays are updated.

🧹 Simplified dependencies
   const allEvents = useMemo(() => {
     return [...latestEvents, ...events]
-  }, [events, latestEvents, latestEvents.length, events.length])
+  }, [events, latestEvents])
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const allEvents = useMemo(() => {
return [...latestEvents, ...events]
}, [events, latestEvents, latestEvents.length, events.length])
const allEvents = useMemo(() => {
return [...latestEvents, ...events]
}, [events, latestEvents])
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/custom/DrawerJobEvents.tsx` around lines 70 - 72, The useMemo
for allEvents includes redundant dependencies (latestEvents.length and
events.length); update the dependency array to only include latestEvents and
events so it becomes useMemo(() => [...latestEvents, ...events], [latestEvents,
events]) — modify the dependency list around the allEvents declaration to remove
the .length entries while keeping the useMemo logic unchanged.

Comment on lines +86 to +92
const refreshVirtualizer = useCallback(() => {
setTimeout(() => {
// TODO find another solution for this timeout issue :
// the allEvent array although is always updated, doesn't trigger the virtualizer getVirtualItems() function to return filled item array
rowVirtualizer.measure()
}, 200)
}, [rowVirtualizer])
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Acknowledged workaround with setTimeout.

The PR notes mention this timeout is needed because the virtualizer doesn't refresh correctly when the event list updates. This is likely related to the key={allEvents.length} forcing remounts and the timing of when the virtualizer measures.

Consider addressing the root cause by:

  1. Removing the key={allEvents.length} from line 149
  2. Using useEffect to call rowVirtualizer.measure() when allEvents changes

This would be more React-idiomatic than the current onOpenChange + setTimeout approach.

Would you like me to help draft an alternative implementation using useEffect to handle virtualizer refresh more reliably?

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/custom/DrawerJobEvents.tsx` around lines 86 - 92, The
workaround using setTimeout in refreshVirtualizer and the remount via
key={allEvents.length} causes unreliable virtualizer updates; remove the
key={allEvents.length} and replace the timeout-based refresh by adding a
useEffect that watches allEvents (or its length/identity) and calls
rowVirtualizer.measure() when it changes (while keeping refreshVirtualizer for
manual triggers if needed); ensure you reference rowVirtualizer.measure and the
allEvents state inside the effect and clean up any stale closures by including
rowVirtualizer in the effect dependency array.

Comment on lines +137 to +146
style={{
height: `${rowVirtualizer.getTotalSize()}px`,
}}
>
{rows.length === 0 ? (
<TableRow>
<TableCell colSpan={columnsLength} className="h-24 text-center">
No results.
</TableCell>
</TableRow>
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Potential layout issue: empty state inside zero-height container.

When rows.length === 0, rowVirtualizer.getTotalSize() returns 0, setting TableBody height to 0px. However, the "No results." row inside has h-24 (6rem) content. The relative positioning on TableBody with explicit height: 0px may clip or hide the empty-state row.

Consider conditionally setting height only when rows exist:

     <TableBody
       className="grid relative"
       style={{
-        height: `${rowVirtualizer.getTotalSize()}px`,
+        height: rows.length > 0 ? `${rowVirtualizer.getTotalSize()}px` : undefined,
       }}
     >
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/features/jobsTable/jobsTable.tsx` around lines 137 - 146, The TableBody
is given a fixed height from rowVirtualizer.getTotalSize(), which is 0 when
rows.length === 0 and causes the "No results." TableRow (h-24) to be clipped;
update the rendering in jobsTable.tsx so the style for TableBody only sets
height when rows exist (e.g., if rows.length > 0 use height:
`${rowVirtualizer.getTotalSize()}px`) or alternatively apply a minHeight (e.g.,
minHeight: '6rem') when rows are empty; locate the TableBody usage that
references rowVirtualizer.getTotalSize() and adjust the conditional styling to
ensure the empty-state TableRow is visible.

Comment thread src/features/jobsTable/jobsTable.tsx
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/components/custom/general/ScrollableList.tsx (1)

71-71: 🧹 Nitpick | 🔵 Trivial

Unused dependency in useCallback.

loadMore is listed in the dependency array but is not used within the loadMoreItems callback body. This causes unnecessary re-creation of the callback when loadMore changes.

♻️ Remove unused dependency
-    [loadMore, loading, loadMoreAction],
+    [loading, loadMoreAction],
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/custom/general/ScrollableList.tsx` at line 71, The useCallback
for loadMoreItems includes an unused dependency loadMore; update the dependency
array of loadMoreItems to only include the actual used symbols (remove loadMore
and keep loading and loadMoreAction) so the callback isn't recreated
unnecessarily, or alternatively reference loadMore inside loadMoreItems if you
intended to use it; locate the useCallback named loadMoreItems in
ScrollableList.tsx and adjust its dependency array accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/components/custom/general/ScrollableList.tsx`:
- Around line 231-244: The sentinel element (loaderRef) must always be present
so the IntersectionObserver can trigger loading; update ScrollableList.tsx to
render the div with ref={loaderRef} unconditionally and only toggle its inner
loader UI based on loading, rather than conditionally rendering the entire
sentinel; ensure the IntersectionObserver callback logic that checks !loading &&
loadMore remains unchanged and will see the always-present sentinel, and remove
the redundant ternary ("opacity-100" / "opacity-0") since the wrapper is always
rendered and only the loader content should change visibility.

---

Outside diff comments:
In `@src/components/custom/general/ScrollableList.tsx`:
- Line 71: The useCallback for loadMoreItems includes an unused dependency
loadMore; update the dependency array of loadMoreItems to only include the
actual used symbols (remove loadMore and keep loading and loadMoreAction) so the
callback isn't recreated unnecessarily, or alternatively reference loadMore
inside loadMoreItems if you intended to use it; locate the useCallback named
loadMoreItems in ScrollableList.tsx and adjust its dependency array accordingly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 7dab4d4c-f812-4d83-b92c-c5d66e16e178

📥 Commits

Reviewing files that changed from the base of the PR and between 62c525a and 6b15b83.

📒 Files selected for processing (2)
  • src/components/custom/DrawerJobFiles.tsx
  • src/components/custom/general/ScrollableList.tsx

Comment thread src/components/custom/general/ScrollableList.tsx Outdated
… at open

Updating duration calculation for latest runs drawer

Updating scrollable list and useLogs hook to better serve the last run drawer
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

♻️ Duplicate comments (2)
src/components/custom/general/ScrollableList.tsx (1)

236-251: 🧹 Nitpick | 🔵 Trivial

Redundant opacity ternary in loading branch.

Line 241's loading ? "opacity-100" : "opacity-0" is always "opacity-100" since this branch only renders when loading is true. The conditional sentinel fix (always having a loaderRef element in DOM) looks correct now.

♻️ Suggested simplification
       {loading ? (
         <div
           ref={loaderRef}
-          className={cn(
-            "flex items-center justify-center py-8",
-            loading ? "opacity-100" : "opacity-0",
-          )}
+          className="flex items-center justify-center py-8"
         >
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/custom/general/ScrollableList.tsx` around lines 236 - 251, In
ScrollableList, the loading branch applies a redundant ternary in the className
("loading ? 'opacity-100' : 'opacity-0'") even though that JSX only renders when
loading is true; replace the conditional with the static class (e.g.,
"opacity-100") or remove the opacity logic entirely and leave the rest of the
classes intact on the element that uses loaderRef to keep the sentinel behavior;
update the element that references loaderRef (and any className uses)
accordingly.
src/components/custom/jobsTable/advancedJobFilteringDialog.tsx (1)

199-201: ⚠️ Potential issue | 🟠 Major

Extract scalar .value instead of returning the full object.

These lines still return the entire FlexibleInput object (values.name, values.cronSetting, values.consumer) instead of extracting the .value property. As noted in the previous review, JobsService expects plain strings, but will receive nested { value, type } objects, causing filterJobs() and exportJobsToJSON() to fail or behave incorrectly.

🔧 Proposed fix
-        name: values.name?.value ? values.name : undefined,
-        cronSetting: values.cronSetting?.value ? values.cronSetting : undefined,
-        consumer: values.consumer?.value ? values.consumer : undefined,
+        name: values.name?.value ? values.name.value : undefined,
+        cronSetting: values.cronSetting?.value
+          ? values.cronSetting.value
+          : undefined,
+        consumer: values.consumer?.value ? values.consumer.value : undefined,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/custom/jobsTable/advancedJobFilteringDialog.tsx` around lines
199 - 201, The filter construction is returning full FlexibleInput objects
(values.name, values.cronSetting, values.consumer) instead of scalar strings;
update the mapping so each property supplies the scalar `.value` when present
(e.g., use values.name?.value ? values.name.value : undefined) for name,
cronSetting, and consumer so JobsService.filterJobs() and exportJobsToJSON()
receive plain strings rather than `{value,type}` objects.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/components/custom/DrawerLatestRuns.tsx`:
- Around line 187-202: The memoized SheetDescription uses the
handleGroupingChange callback but its useMemo deps are empty, causing a stale
closure; update the dependency array for SheetDescription to include
handleGroupingChange (and any other callbacks/values it uses, e.g., groupingKey
if referenced) so ManagedSelect receives the current handler (identify
SheetDescription, handleGroupingChange, ManagedSelect, and GroupingOptions in
your diff and add those deps to the useMemo dependency array).
- Around line 145-152: The getLatestRuns callback has unnecessary dependencies
(loading, setItemDateArray, LogItems) causing needless re-creations; update its
dependency array to only include the values actually read inside the callback
(e.g., inputSchema, itemDateArray, groupingKey) and remove loading,
setItemDateArray, and LogItems from the array so React won't recreate
getLatestRuns unnecessarily—locate the getLatestRuns definition and adjust the
dependency list accordingly.
- Around line 177-185: handleGroupingChange is using useCallback with the wrong
dependency (groupingKey) even though it doesn't read it; update the dependency
array to reference resetDrawer instead. Locate the handleGroupingChange callback
that calls setGroupingKey, resetDrawer(), and
scrollableListRef?.current?.resetList() and change its dependency list from
[groupingKey] to [resetDrawer] so the callback is only recreated when
resetDrawer changes; do not add scrollableListRef since refs are stable.

In `@src/components/custom/general/ScrollableList.tsx`:
- Around line 123-126: The resetList useCallback lists items and
currentFocusIndex as dependencies even though the function only calls
setItems([]) and setCurrentFocusIndex(0); remove items and currentFocusIndex
from the dependency array and leave only the setter references (or an empty
array if setters are stable) so resetList is not needlessly re-created; update
the dependency array for resetList accordingly (refer to the resetList function
and the setItems/setCurrentFocusIndex state setters).

In `@src/components/sheet-action-dialog.tsx`:
- Around line 47-50: The detection for React elements uses redundant typeof
checks; simplify by making isTitleElement = isValidElement(props.title) and
isDescriptionElement = isValidElement(props.description) (remove the unnecessary
typeof ... !== "string" guards) so the element checks rely solely on React's
isValidElement helper (update the variables isTitleElement and
isDescriptionElement accordingly).

In `@src/lib/socketUtils.ts`:
- Around line 238-243: The hook's return value in socketUtils.ts no longer
includes latestLogs, which breaks consumers like eventLog.tsx that destructure
latestLogs and call latestLogs.map; re-add latestLogs to the returned object
(alongside subscribeToEvents, unsubscribeFromEvents, logs, logsLoading) so
callers receive the array; ensure the identifier latestLogs used in the return
matches the variable declared/updated within the hook to avoid shadowing or
undefined returns.

In `@src/utils/dateUtils.ts`:
- Around line 36-40: formatDiff currently assumes callers pass Date objects and
will return NaN if given strings; update formatDiff to validate and/or coerce
inputs: check that date1 and date2 are valid Date instances (using instanceof
Date and !isNaN(date.getTime())) and if strings are accepted, attempt new
Date(string) conversion, then handle invalid dates by returning a sensible
fallback (e.g., 'invalid date' or 0 sec) or throwing a clear error; also add a
short JSDoc to formatDiff describing expected parameter types so callers (e.g.,
DrawerLatestRuns.tsx) know to pass Dates or strings.
- Around line 48-54: The logic computing human-readable span uses
differenceInHours/differenceInDays and currently returns hours only when hours <
2, causing 2–23 hour spans to fall through and render as "0 d"; update the
conditional around the computed hours variable (from using hours < 2) to use
hours < 24 so durations under a day render as `${hours} h`, and only
compute/return `${days} d` from differenceInDays when hours >= 24.

---

Duplicate comments:
In `@src/components/custom/general/ScrollableList.tsx`:
- Around line 236-251: In ScrollableList, the loading branch applies a redundant
ternary in the className ("loading ? 'opacity-100' : 'opacity-0'") even though
that JSX only renders when loading is true; replace the conditional with the
static class (e.g., "opacity-100") or remove the opacity logic entirely and
leave the rest of the classes intact on the element that uses loaderRef to keep
the sentinel behavior; update the element that references loaderRef (and any
className uses) accordingly.

In `@src/components/custom/jobsTable/advancedJobFilteringDialog.tsx`:
- Around line 199-201: The filter construction is returning full FlexibleInput
objects (values.name, values.cronSetting, values.consumer) instead of scalar
strings; update the mapping so each property supplies the scalar `.value` when
present (e.g., use values.name?.value ? values.name.value : undefined) for name,
cronSetting, and consumer so JobsService.filterJobs() and exportJobsToJSON()
receive plain strings rather than `{value,type}` objects.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 7ef01a4c-e8e5-482f-98c4-120a962edfb5

📥 Commits

Reviewing files that changed from the base of the PR and between 6b15b83 and 137a53a.

📒 Files selected for processing (7)
  • src/components/custom/DrawerLatestRuns.tsx
  • src/components/custom/ManagedSelect.tsx
  • src/components/custom/general/ScrollableList.tsx
  • src/components/custom/jobsTable/advancedJobFilteringDialog.tsx
  • src/components/sheet-action-dialog.tsx
  • src/lib/socketUtils.ts
  • src/utils/dateUtils.ts

Comment on lines +145 to +152
[
inputSchema,
loading,
itemDateArray,
setItemDateArray,
LogItems,
groupingKey,
],
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Excessive dependencies in getLatestRuns callback.

loading, setItemDateArray, and LogItems are listed as dependencies but aren't read inside the callback. loading is only checked externally, setItemDateArray is a stable setter, and LogItems isn't used at all. This causes unnecessary re-creations and potential stale closure issues.

♻️ Suggested fix
     [
       inputSchema,
-      loading,
       itemDateArray,
-      setItemDateArray,
-      LogItems,
       groupingKey,
     ],
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
[
inputSchema,
loading,
itemDateArray,
setItemDateArray,
LogItems,
groupingKey,
],
[
inputSchema,
itemDateArray,
groupingKey,
],
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/custom/DrawerLatestRuns.tsx` around lines 145 - 152, The
getLatestRuns callback has unnecessary dependencies (loading, setItemDateArray,
LogItems) causing needless re-creations; update its dependency array to only
include the values actually read inside the callback (e.g., inputSchema,
itemDateArray, groupingKey) and remove loading, setItemDateArray, and LogItems
from the array so React won't recreate getLatestRuns unnecessarily—locate the
getLatestRuns definition and adjust the dependency list accordingly.

Comment on lines +177 to +185
const handleGroupingChange = useCallback(
value => {
setGroupingKey(value)
// resetting list via ref
resetDrawer()
scrollableListRef?.current?.resetList()
},
[groupingKey],
)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Incorrect dependency in handleGroupingChange.

The callback lists groupingKey as a dependency but doesn't read its previous value—it only sets a new value. This causes unnecessary re-creation. The actual dependency should be resetDrawer.

♻️ Suggested fix
   const handleGroupingChange = useCallback(
     value => {
       setGroupingKey(value)
       // resetting list via ref
       resetDrawer()
       scrollableListRef?.current?.resetList()
     },
-    [groupingKey],
+    [resetDrawer],
   )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/custom/DrawerLatestRuns.tsx` around lines 177 - 185,
handleGroupingChange is using useCallback with the wrong dependency
(groupingKey) even though it doesn't read it; update the dependency array to
reference resetDrawer instead. Locate the handleGroupingChange callback that
calls setGroupingKey, resetDrawer(), and scrollableListRef?.current?.resetList()
and change its dependency list from [groupingKey] to [resetDrawer] so the
callback is only recreated when resetDrawer changes; do not add
scrollableListRef since refs are stable.

Comment on lines +187 to +202
const SheetDescription = useMemo(() => {
return (
<div className="flex justify-between items-center !my-0">
<div>List of previous job run logs</div>
<div>
<ManagedSelect
exportOnlyValue={true}
onChange={handleGroupingChange}
inputOptions={GroupingOptions}
defaultValue="week"
className="focus:ring-0 focus-visible:ring-0 border-2 border-border"
/>
</div>
</div>
)
}, [])
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Stale closure: SheetDescription useMemo has empty dependency array but uses handleGroupingChange.

The handleGroupingChange callback is used inside the memoized component but not listed as a dependency. This means the ManagedSelect will always use the initial (stale) version of handleGroupingChange, which captures the initial groupingKey value. Grouping changes after the first may not work correctly.

🐛 Proposed fix: Add handleGroupingChange to dependencies
     )
-  }, [])
+  }, [handleGroupingChange])
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const SheetDescription = useMemo(() => {
return (
<div className="flex justify-between items-center !my-0">
<div>List of previous job run logs</div>
<div>
<ManagedSelect
exportOnlyValue={true}
onChange={handleGroupingChange}
inputOptions={GroupingOptions}
defaultValue="week"
className="focus:ring-0 focus-visible:ring-0 border-2 border-border"
/>
</div>
</div>
)
}, [])
const SheetDescription = useMemo(() => {
return (
<div className="flex justify-between items-center !my-0">
<div>List of previous job run logs</div>
<div>
<ManagedSelect
exportOnlyValue={true}
onChange={handleGroupingChange}
inputOptions={GroupingOptions}
defaultValue="week"
className="focus:ring-0 focus-visible:ring-0 border-2 border-border"
/>
</div>
</div>
)
}, [handleGroupingChange])
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/custom/DrawerLatestRuns.tsx` around lines 187 - 202, The
memoized SheetDescription uses the handleGroupingChange callback but its useMemo
deps are empty, causing a stale closure; update the dependency array for
SheetDescription to include handleGroupingChange (and any other callbacks/values
it uses, e.g., groupingKey if referenced) so ManagedSelect receives the current
handler (identify SheetDescription, handleGroupingChange, ManagedSelect, and
GroupingOptions in your diff and add those deps to the useMemo dependency
array).

Comment on lines +123 to +126
const resetList = useCallback(() => {
setItems([])
setCurrentFocusIndex(0)
}, [items, currentFocusIndex])
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Remove unused dependencies from resetList callback.

The items and currentFocusIndex values are not used inside the callback body—they are only being set, not read. Including them as dependencies causes unnecessary re-creation of the callback.

♻️ Suggested fix
   const resetList = useCallback(() => {
     setItems([])
     setCurrentFocusIndex(0)
-  }, [items, currentFocusIndex])
+  }, [])
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const resetList = useCallback(() => {
setItems([])
setCurrentFocusIndex(0)
}, [items, currentFocusIndex])
const resetList = useCallback(() => {
setItems([])
setCurrentFocusIndex(0)
}, [])
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/custom/general/ScrollableList.tsx` around lines 123 - 126, The
resetList useCallback lists items and currentFocusIndex as dependencies even
though the function only calls setItems([]) and setCurrentFocusIndex(0); remove
items and currentFocusIndex from the dependency array and leave only the setter
references (or an empty array if setters are stable) so resetList is not
needlessly re-created; update the dependency array for resetList accordingly
(refer to the resetList function and the setItems/setCurrentFocusIndex state
setters).

Comment thread src/components/sheet-action-dialog.tsx Outdated
Comment on lines +47 to +50
const isTitleElement =
isValidElement(props.title) && typeof props.title !== "string"
const isDescriptionElement =
isValidElement(props.description) && typeof props.description !== "string"
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Redundant type check in element detection.

The typeof props.title !== "string" check is unnecessary since isValidElement() already returns false for primitive strings. The same applies to isDescriptionElement.

♻️ Suggested simplification
-  const isTitleElement =
-    isValidElement(props.title) && typeof props.title !== "string"
-  const isDescriptionElement =
-    isValidElement(props.description) && typeof props.description !== "string"
+  const isTitleElement = isValidElement(props.title)
+  const isDescriptionElement = isValidElement(props.description)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const isTitleElement =
isValidElement(props.title) && typeof props.title !== "string"
const isDescriptionElement =
isValidElement(props.description) && typeof props.description !== "string"
const isTitleElement = isValidElement(props.title)
const isDescriptionElement = isValidElement(props.description)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/sheet-action-dialog.tsx` around lines 47 - 50, The detection
for React elements uses redundant typeof checks; simplify by making
isTitleElement = isValidElement(props.title) and isDescriptionElement =
isValidElement(props.description) (remove the unnecessary typeof ... !==
"string" guards) so the element checks rely solely on React's isValidElement
helper (update the variables isTitleElement and isDescriptionElement
accordingly).

Comment thread src/lib/socketUtils.ts
Comment thread src/utils/dateUtils.ts
Comment on lines +36 to +40
export function formatDiff(date1: Date, date2: Date) {
const seconds = Math.abs(differenceInSeconds(date1, date2))

if (seconds < 60) {
return `${seconds} sec`
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Consider adding input validation or documentation for Date parameters.

Per the relevant context snippets, jobLog from the API has start_time and end_time as strings. While DrawerLatestRuns.tsx correctly converts them to Date objects before calling formatDiff, other callers might pass raw strings, which would produce NaN results from differenceInSeconds.

🛡️ Suggested defensive validation
 export function formatDiff(date1: Date, date2: Date) {
+  if (!(date1 instanceof Date) || !(date2 instanceof Date)) {
+    throw new TypeError("formatDiff expects Date objects")
+  }
   const seconds = Math.abs(differenceInSeconds(date1, date2))
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export function formatDiff(date1: Date, date2: Date) {
const seconds = Math.abs(differenceInSeconds(date1, date2))
if (seconds < 60) {
return `${seconds} sec`
export function formatDiff(date1: Date, date2: Date) {
if (!(date1 instanceof Date) || !(date2 instanceof Date)) {
throw new TypeError("formatDiff expects Date objects")
}
const seconds = Math.abs(differenceInSeconds(date1, date2))
if (seconds < 60) {
return `${seconds} sec`
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/utils/dateUtils.ts` around lines 36 - 40, formatDiff currently assumes
callers pass Date objects and will return NaN if given strings; update
formatDiff to validate and/or coerce inputs: check that date1 and date2 are
valid Date instances (using instanceof Date and !isNaN(date.getTime())) and if
strings are accepted, attempt new Date(string) conversion, then handle invalid
dates by returning a sensible fallback (e.g., 'invalid date' or 0 sec) or
throwing a clear error; also add a short JSDoc to formatDiff describing expected
parameter types so callers (e.g., DrawerLatestRuns.tsx) know to pass Dates or
strings.

Comment thread src/utils/dateUtils.ts
@moda20 moda20 changed the title [DEV-IMP] Dashboard UI Enhancements and Performance Improvements [MAIN-IMP] Dashboard UI Enhancements and Performance Improvements Apr 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant